把一組數據結構和處理它們的方法組成物件 (object),把相同行為的對象歸納為類別 (class),通過 class 的封裝 (encapsulation) 隱藏內部細節,透過繼承 (inheritance) 實現 class 的特化 (specialization) 和泛化 (generalization),通過多型 (polymorphism) 實現基於物件類別的動態分配。
class 是 object 的藍圖和模板,而 object 是 class 的實例。class 是抽象的概念,而 object 是具體的東西。
在物件導向編程的中,一切皆為 object,object 都有屬性和行為。
每個 object 都是獨一無二的,而且 object 一定屬於某個 class。
當我們把一堆擁有共同特徵的 object 的靜態特徵 (屬性)和動態特徵(行為)都取出來後,就可以定義出一個 class。
使用 class 關鍵字定義 class,然後在 class 中透過函數來定義方法 (Method),這樣就可以將 object 的動態特徵描述出來。
class Student(object):
# __init__是一個特殊方法用於創建 object 時進行初始化
# 我們可以為學生這個 object 綁定 name 和 age 兩個屬性
def __init__ (self, name, age):
self.name = name
self.age = age
def study (self, course_name):
print('%s 正在學習 %s' % (self.name, course_name))
def watch_movie(self):
if self.age < 18:
print('%s 只能觀看普通電影' % self.name)
else:
print('%s 能觀看所有電影' % self.name)
def main():
stu1 = Student('Andy', 38) # 創建學生並指定姓名和年齡
stu1.study('Python 程式設計') # 給 object 發 study 的消息
stu1.watch_movie() # 给 object 發 watch_movie 的消息
stu2 = Student('Amy', 15)
stu2.study('C 語言程式設計')
stu2.watch_movie()
if __name__ == '__main__':
main()
在物件導向編程中,通常會將 object 的屬性設為私有的 (private) 或受保護的 (protected),也就是不允許外界訪問,而 object 的 method 通常都是公開的 (public),因為公開的 method 就是 object 能夠接受的消息。
在 Python 中,屬性 和 method 的訪問權限只有兩種,公開和私有的,如果希望屬性是私有的,在給屬性命名時可以用兩個下劃線 ( __ ) 作為開頭。
class Test:
def __init__ (self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
# AttributeError: 'Test' object has no attribute '__bar'
test.__bar()
# AttributeError: 'Test' object has no attribute '__foo'
print(test.__foo)
if __name__ == "__main__":
main()
但是,Python並沒有從語法上嚴格保證私有屬性或方法的私密性,如果你知道更換名字的規則仍然可以訪問到它們。
在實際開發中,並不建議將屬性設置為私有的,因為這會導致子類別無法訪問。
所以大多 Python 會遵循一種命名慣例就是讓屬性名以單下劃線 ( _ ) 開頭來表示屬性是受保護的,這種做法並不是語法上的規則,單下劃線開頭的屬性和方法外界仍然是可以訪問的。
class Test:
def __init__ (self, foo):
self.__foo = foo
def __bar(self):
print(self.__foo)
print('__bar')
def main():
test = Test('hello')
test._Test__bar()
print(test._Test__foo)
if __name__ == "__main__":
main()
物件導向有三大支柱:封裝、繼承和多型。
封裝:將 object 內部的資料隱藏起來,只向外界提供簡單的編程介面 (interface)。
在 class 中定義的 method 其實就是把數據和對數據的操作封裝起來,其他 object 即無法瞭解此 object 的內部細節。
在我們創建了 object 之後,只需要給 object 發送一個消息(調用 method)就可以執行當中的程式碼。
也就是說只需要知道 method 的名字和傳入的參數,而不需要知道方法內部的實現細節。
簡白的說,對一件事情只需要理解他的外在就好,不需要了解裡面內部的構造。
練習1 - 定義一個 class 描述數字時鐘。
from time import sleep
class Clock(object):
def __init__ (self, hour = 0, minute = 0, second = 0): # 初始化
self._hour = hour # 時
self._minute = minute # 分
self._second = second # 秒
def run(self):
self._second += 1 # 秒數 + 1
if self._second == 60: # 當秒數為 60, 分數 + 1, 秒數變回 0
self._second = 0
self._minute += 1
if self._minute == 60: # 當分數為 60, 時數 + 1, 分數變回 0
self._minute = 0
self._hour += 1
if self._hour == 24: # 當時數為 24, 時數變為 0
self._hour = 0
def show(self): # 顯示時間
return '%02d:%02d:%02d' % (self._hour, self._minute, self._second)
def main():
clock = Clock(23, 59, 58)
while True:
print(clock.show())
# sleep()函數推遲程式運行,單位為 secs 秒數
sleep(1) # 推遲進行 1 秒
clock.run()
if __name__ == '__main__':
main()
練習2 - 定義一個 class 描述平面上的點並提供移動點和計算到另一個點距離的方法。
from math import sqrt
class Point(object):
def __init__ (self, x = 0, y = 0): # 初始化
self.x = x # x 座標
self.y = y # y 座標
def move_to (self, x, y): # 移動到指定位置
self.x = x # x 座標
self.y = y # y 座標
def move_by (self, dx, dy): # 移動指定的增量
self.x += dx # x 座標的增量
self.y += dy # y 座標的增量
def distance_to (self, other): # 計算兩點的距離 other:另一個點
dx = self.x - other.x
dy = self.y - other.y
return sqrt(dx ** 2 + dy ** 2)
def __str__(self):
return '(%s, %s)' % (str(self.x), str(self.y))
def main():
p1 = Point(3, 5)
p2 = Point() # (0, 0)
print(p1)
print(p2)
p1.move_to(-1, 2)
p2.move_by(3, 5)
print(p1)
print(p2)
print(p1.distance_to(p2))
if __name__ == '__main__':
main()